﻿namespace Jiubang.Tracer{
	using System;
	using System.Runtime.InteropServices;

	internal class ThreadCycleTimeTracer : PrefTracer{
        private static readonly GetCycleTimeDelegate getCycleTime;

		/// <summary>
		/// 是否是Vista或者Server2008等高版本
		/// </summary>
		private static readonly bool isVistaOrServer2K8 = Environment.OSVersion.Version.Major > 5;

        static ThreadCycleTimeTracer()
        {
            getCycleTime = isVistaOrServer2K8 ? QueryThreadCycleTimeVista : new GetCycleTimeDelegate(QueryThreadCycleTimeXp);

        }


		private ulong cycleTime;


		public ThreadCycleTimeTracer(string name, ILogger logger) : base(name, logger){

			cycleTime = getCycleTime();
		}

		/// <summary>
		/// 重置计数器
		/// </summary>
		public override void Reset(){
			cycleTime = getCycleTime();
		}

		/// <summary>
		/// 检查从启动、最近一次Reset或check执行到当前位置测度数据是否超过期望值。
		/// 如是，将实际测量值与标签追加至Logger
		/// </summary>
		/// <param name="onLoggingCallback">实际测量值超过期望值时调用该回调方法获取标签</param>
		/// <param name="expectedDifference">期望差值。如当前值-重置值&gt;该值将被日志记录</param>
		/// <returns></returns>
		public override short Check(OnLoggingCallback onLoggingCallback, short expectedDifference){
			if (expectedDifference < 0)
				throw new ArgumentOutOfRangeException("expectedDifference", expectedDifference, "必须为非负数");
			ulong lastCycleTime = cycleTime;
			cycleTime = getCycleTime();

			ulong actual = (cycleTime - lastCycleTime);
			ulong expected = (ulong) expectedDifference;
			short returnValue = (short) Math.Min(actual, (ulong) short.MaxValue);
			if (actual > expected)
				Logger.Log(this, "TC:" + onLoggingCallback(), expectedDifference, returnValue);
			return returnValue;
		}

		private static ulong QueryThreadCycleTimeXp(){
			long l;
			long kernelTime, userTimer;
			GetThreadTimes(GetCurrentThread(), out l, out l, out kernelTime, out userTimer);
			return Math.Min(ulong.MaxValue, (ulong) (kernelTime + userTimer));
		}


		private static ulong QueryThreadCycleTimeVista(){
			ulong l = 0;
			QueryThreadCycleTime(GetCurrentThread(), ref l);
			return l;
		}


		/// <summary>
		/// Vista专用
		/// </summary>
		/// <param name="threadHandle"></param>
		/// <param name="cycleTime"></param>
		/// <returns></returns>
		[DllImport("kernel32.dll")]
		[return : MarshalAs(UnmanagedType.Bool)]
		private static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);


		/// <summary>
		/// XP专用
		/// </summary>
		/// <param name="hThread"></param>
		/// <param name="lpCreationTime"></param>
		/// <param name="lpExitTime"></param>
		/// <param name="lpKernelTime"></param>
		/// <param name="lpUserTime"></param>
		/// <returns></returns>
		[DllImport("kernel32.dll", SetLastError = true)]
		private static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime,
		                                          out long lpUserTime);

		[DllImport("kernel32.dll")]
		private static extern IntPtr GetCurrentThread();

		#region Nested type: GetCycleTimeDelegate

		private delegate ulong GetCycleTimeDelegate();

		#endregion
	}
}